package com.huangtang.pc.util;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

/**
 * 雪花Id生成工具类
 *
 * @author sunjf
 * @date 8/26/22
 */
@Slf4j
@Component
@SuppressWarnings("unused")
public class SnowflakeIdUtil {

    /**
     * 数据中心ID-机房ID
     */
    private static final long DATA_CENTER_ID;

    /**
     * 机器ID
     */
    private static final long WORKER_ID;

    private static final String AT = "@";

    static {
        long maxDataCenterId = 31L;
        DATA_CENTER_ID = getDataCenterId(maxDataCenterId);
        WORKER_ID = getWorkerId();
        log.info("SnowflakeIdUtil dataCenterId:{}, workerId:{}", DATA_CENTER_ID, WORKER_ID);
    }

    private SnowflakeIdUtil() {
        log.info("SnowflakeIdUtil initialization is complete");
    }

    public static InetAddress getLocalHostExactAddress() {
        try {
            InetAddress candidateAddress = null;
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = networkInterfaces.nextElement();
                //该网卡接口下的ip会有多个，也需要一个个的遍历，找到自己所需要的
                for (Enumeration<InetAddress> enumeration = networkInterface.getInetAddresses(); enumeration.hasMoreElements(); ) {
                    InetAddress inetAddr = enumeration.nextElement();
                    //排除loopback回环类型地址（不管是IPv4还是IPv6 只要是回环地址都会返回true）
                    if (!inetAddr.isLoopbackAddress()) {
                        if (inetAddr.isSiteLocalAddress()) {
                            /*
                             * 如果是site-local地址，就是它了 就是我们要找的
                             * 绝大部分情况下都会在此处返回你的ip地址值
                             */
                            return inetAddr;
                        }
                        if (candidateAddress == null) {
                            candidateAddress = inetAddr;
                        }
                    }
                }
            }
            //若不是site-local地址 那就记录下该地址当作候选
            return candidateAddress == null ? InetAddress.getLocalHost() : candidateAddress;
        } catch (Exception e) {
            log.warn("IP Address Obtaining exception:" + e.getMessage(), e);
        }
        return null;
    }

    private static long getDataCenterId(long maxDataCenterId) {
        long id = 0L;
        try {
            //获取本机(或者服务器ip地址)
            InetAddress inetAddress = getLocalHostExactAddress();
            if (inetAddress == null) {
                throw new RuntimeException("ip is null");
            }
            log.info("SnowflakeIdUtil ip:{}", inetAddress.getHostAddress());
            NetworkInterface network = NetworkInterface.getByInetAddress(inetAddress);
            //一般不是null会进入else
            if (network == null) {
                id = 1L;
            } else {
                //获取物理网卡地址
                byte[] mac = network.getHardwareAddress();
                if (null != mac) {
                    id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
                    id = id % (maxDataCenterId + 1);
                }
            }
        } catch (Exception e) {
            log.warn("SnowflakeIdUtil getDataCenterId: " + e.getMessage());
        }
        return id;
    }

    private static long getWorkerId() {
        long maxWorkerId = 31L;
        StringBuilder sb = new StringBuilder();
        sb.append(DATA_CENTER_ID);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (StrUtil.isEmpty(name)) {
            /*
             * GET jvmPid
             */
            sb.append(name.split(AT)[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (sb.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 这里使用了hutool工具来获取ID
     * @return SnowflakeId
     */
    public static String getSnowflakeId() {
        return IdUtil.getSnowflake(WORKER_ID, DATA_CENTER_ID).nextIdStr();
    }
}
